import { LunarBase } from "./base";
import { solarToLunar, traditionalChineseFolkFestivals } from "./solarToLunar";
const { heavenly, earthly, zodiac } = LunarBase;

interface ILunarDate {
  /**
   * 农历年
   */
  heavenl: string;

  /**
   * 十二生肖
   */
  zodiac: string;

  /**
   * 农历日期
   */
  lunar: string;

  /** 节日；
   * 二十四节气（24）+ 中国传统民俗节日（8-1）
   */
  festivals?: string;

  /**
   * 是否是中国传统节日
   */
  isFestivals: boolean;
}

/** 中国日历；
 * 农历；天干地支；十二生肖；
 */
export class ChinaCalendar {
  /** 农历类的构造函数
   *
   * @param currentDate 当前日期
   */
  constructor(currentDate: Date | string) {
    let date = currentDate;
    if (typeof date === "string") {
      date = new Date(date);
    }
    this._date = date;
  }

  /** 日期 */
  private _date: Date;

  toLunarInfo(): ILunarDate {
    const { lunarMonth, lunarDay, solarTerm } = solarToLunar(this._date);
    const h: number = (this.y - 4) % 10;
    const e: number = (this.y - 4) % 12;

    const lunar = lunarMonth + "月" + lunarDay;
    let isFestivals: boolean;
    let festivals: string | undefined;
    if (traditionalChineseFolkFestivals(lunar) != null) {
      isFestivals = true;
      festivals = traditionalChineseFolkFestivals(lunar)!;
    } else if (solarTerm != null) {
      isFestivals = true;
      festivals = solarTerm;
    } else {
      isFestivals = false;
      festivals = undefined;
    }
    return {
      heavenl: heavenly[h] + earthly[e],
      zodiac: zodiac[e],
      lunar,
      festivals,
      isFestivals,
    };
  }

  //#region 计算属性； 年、月、日、星期几
  get y() {
    return this._date.getFullYear();
  }
  get m() {
    return this._date.getMonth();
  }
  get d() {
    return this._date.getDate();
  }

  get week() {
    return ChinaCalendar.caculateWeekDay(this.y, this.m, this.d);
  }

  /** 获取天干地支 */
  get heavenlYearthly(): string {
    const h: number = (this.y - 4) % 10;
    const e: number = (this.y - 4) % 12;
    if (this.y > 3) {
      return heavenly[h] + earthly[e] + "年，生肖[" + zodiac[e] + "],";
    } else {
      // throw new Error("无效的年份！！");
      return "";
    }
  }
  //#endregion

  //#region  类的静态函数
  
  /** 是否是闰年 */
  static isLeapYear(year: number) {
    if (year < 1600) {
      return year % 4 === 0;
    }
    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
  }

  /** 
   * 根据年月日计算礼拜几；
   * 基姆拉尔森计算公式
   * @param y 年
   * @param m 月
   * @param d 日
   * @returns 礼拜几
   */
  static caculateWeekDay = (y: number, m: number, d: number): string => {
    if (m == 1) (m = 13), y--;
    if (m == 2) (m = 14), y--;
    let week =
      (d + 2 * m + (3 * (m + 1)) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
    let weekStr: string = "";
    switch (week) {
      case 0:
        weekStr = "星期一";
        break;
      case 1:
        weekStr = "星期二";
        break;
      case 2:
        weekStr = "星期三";
        break;
      case 3:
        weekStr = "星期四";
        break;
      case 4:
        weekStr = "星期五";
        break;
      case 5:
        weekStr = "星期六";
        break;
      case 6:
        weekStr = "星期日";
        break;
    }
    return weekStr;
  };

  /** 获取时间并格式化函数
   *
   * @param M 格式模板 如: YYYY-MM-DD ...
   * @param Time 可选传入时间参数 默认为 Now
   * @returns
   */
  static getDate = (M: string, Time: Date | null | string | number = null) => {
    let D: Date = Time ? new Date(Time) : new Date();
    let P: RegExp =
      /(Y{2,4}|M{1,2}|D{1,2}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|C{1,2}|W{1,2})/g;
    let ToZ: any = (S: string | number) => (Number(S) < 10 ? "0" + S : S);
    let DS: string = M.replace(P, ($0: string) => {
      switch ($0) {
        case "YY":
          return D.getFullYear().toString().slice(-2);
        case "YYYY":
          return D.getFullYear();
        case "M":
          return D.getMonth() + 1;
        case "MM":
          return ToZ(D.getMonth() + 1);
        case "D":
          return D.getDate();
        case "DD":
          return ToZ(D.getDate());
        case "W":
          return D.getDay();
        case "WW":
          return ["日", "一", "二", "三", "四", "五", "六"][D.getDay()];
        case "H":
          return D.getHours();
        case "HH":
          return ToZ(D.getHours());
        case "m":
          return D.getMinutes();
        case "mm":
          return ToZ(D.getMinutes());
        case "s":
          return D.getSeconds();
        case "ss":
          return ToZ(D.getSeconds());
        case "C":
          return Math.trunc(D.getTime() / 1000);
        case "CC":
          return D.getTime();
        default:
          return $0;
      }
    });
    return DS;
  };
  //#endregion
}
